﻿package kliment.controls {
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	import flash.ui.Keyboard;
	import flash.utils.clearInterval;
	import flash.utils.setInterval;
	import kliment.display.MovieClipButton;
	import kliment.events.MouseGestureEvent;
	import kliment.events.MovieClipButtonEvent;
	import kliment.math.Calc;
	import kliment.utils._Array;
	import kliment.utils._Sprite;
	
	/**
	 * ...
	 * @author	Kliment
	 * @version	1.5.27
	 * 
	 * class name: kliment.controls.Scroller
	 */
	public final class Scroller extends ControlsAbstract{		
		static private var _spaces_mcArray:Array;
		private var _intervalId:uint;
		private var _intervalIdM:uint;
		private var _intervalIdUpdate:uint;
		
		private var _seeker_step:Number = 2;
		private var _move_step:Number = .1;
		private var _delay:Number = 25;
		private var _delay_button:Number = 50;
		private var _min_height_seeker:Number = 5;
		private var _ratio:Number = .4;
		private var _scroll_part:Number = .25;
		private var _stepPixels:Number;
		private var _stepInPixels:Boolean = false;
		private var _keepMouse:Boolean = false;
		private var _scaleSeeker:Boolean = false;
		private var _keyboardEnable:Boolean = true;
		private var _rounded:Boolean = true;
		private var _stage:Stage;
		private var _target_size:Number = 0;
		
		private var _orientation:*;
		private var _x_y_mouse:String = 'mouseY';
		
		private var _position:Number = 0;
		private var _target_mc:Sprite;
		private var _target_mask_mc:Sprite;
		private var _scrollerBox:ScrollerBox;
		private var _aMouseGesture:MouseGesture;
		private var _mouseGestureEnable:Boolean = true;
		
		/**
		 * 
		 * @param	target_mc Sprite
		 * @param	target_mask_mc Sprite
		 * @param	options Object {scrollerBox:ScrollerBox, ratio:Number, orientation:*, stepPixels:Number, keepMpuse:Boolean}
		 */
		public function Scroller(target_mc:Sprite, target_mask_mc:Sprite, options:Object = null) {
			_target_mc = target_mc;
			_target_mask_mc = target_mask_mc;
			
			if (_target_mc.stage) {
				_stage = _target_mc.stage;
			} else {
				_target_mc.addEventListener(Event.ADDED_TO_STAGE, _addToStageHandler);
			}
			
			if (!_spaces_mcArray)
				_spaces_mcArray = new Array();
			_spaces_mcArray.push(_target_mask_mc);
			
			_aMouseGesture = new MouseGesture(_target_mask_mc, 300, 200);
			_aMouseGesture.addEventListener(MouseGestureEvent.START, _mouseGestureHandler);
			_aMouseGesture.addEventListener(MouseGestureEvent.SLIDE_DOWN, _mouseGestureHandler);
			_aMouseGesture.addEventListener(MouseGestureEvent.SLIDE_UP, _mouseGestureHandler);
			_aMouseGesture.addEventListener(MouseGestureEvent.SLIDE_RIGHT, _mouseGestureHandler);
			_aMouseGesture.addEventListener(MouseGestureEvent.SLIDE_LEFT, _mouseGestureHandler);
			_aMouseGesture.addEventListener(MouseGestureEvent.DRAG, _mouseGestureHandler);
			
			if (options)
				setOptions(options);
			
			_setControlActions();
			update();
		}
		
		private function _addToStageHandler(event:Event):void {
			_target_mc.removeEventListener(Event.ADDED_TO_STAGE, _addToStageHandler);
			_stage = _target_mc.stage;
			update();
		}
		
		/**
		 * 
		 * @param	options Object {scrollerBox:ScrollerBox, rate:Number, orientation, scaleSeeker:Boolean, stepPixels:Number, mouseGestureEnable:Boolean, keyboardEnable:Boolean, noMask:Boolean}
		 */
		public function setOptions(options:Object):void {
			_scrollerBox = options.scrollerBox;
			if (_scrollerBox)
				_scrollerBox.scroller = this;
			
			if (options.noMask == null)
				_target_mc.mask = _target_mask_mc;
			else 
				_target_mc.mask = null;
				
			if (options.rounded != null)
				_rounded = options.rounded;
			
			if (options.keepMouse != null)
				_keepMouse = options.keepMouse;
				
			if (options.scaleSeeker != null)
				_scaleSeeker = options.scaleSeeker;
			
			if (options.min_height_seeker != null)
				_min_height_seeker = options.min_height_seeker;
				
			if (options.keyboardEnable != null)
				_keyboardEnable = options.keyboardEnable;
				
			if (options.mouseGestureEnable != null)
				_mouseGestureEnable = options.mouseGestureEnable;
				
			if (!isNaN(options.ratio)) 
				_ratio = options.ratio;
			
			_set_orientation(options.orientation || Orientation.VERTICAL);
			
			if (options.stepPixels != null) 
				setPixelStep(options.stepPixels);
			
			if (options.update != null) 
				update(options.update);
			else
				update();
		}
		
		public function moveUp():void {
			if (_scrollerBox.seeker_btn && _scrollerBox.space_btn) 
				_moveSeeker(-_seeker_step);
			else 
				move(_position - _move_step);
		}
		
		public function moveDown():void {
			if (_scrollerBox.seeker_btn && _scrollerBox.space_btn) 
				_moveSeeker(_seeker_step);
			else 
				move(_position + _move_step);
		}
		
		public function move(percent:Number):void {
			percent = Math.max(0, Math.min(percent, 1));
			_position = percent;
		}
		
		public function showPart(startPix:Number, endPix:Number):void {
			var partLength:Number = startPix + endPix;
			var _target_mask_W_H:Number = _getWidth_Height(_target_mask_mc);
			var _target_X_Y:Number = _getX_Y(_target_mc);
			var _target_mask_X_Y:Number = _getX_Y(_target_mask_mc);
			
			var to:Number;
			
			var leftStart:Number = _target_X_Y + startPix + _target_mask_X_Y;
			if (leftStart < _target_mask_X_Y) {
				to = startPix / _content_difference_size();
			}
			
			var rihtStop:Number = _target_X_Y + endPix + _target_mask_X_Y;
			if (rihtStop > _target_mask_X_Y + _target_mask_W_H) {
				to = (_target_mask_W_H - endPix) / _content_difference_size();
			}
			
			if (!isNaN(to)) {
				positionPercent(Math.abs(to));
			}
		}
		
		public function setPixelStep(value:Number):void {
			if (_stepPixels != value && value != 0) {
				_stepInPixels = true;
				_stepPixels = value;
			} else {
				_stepInPixels = false;
				_stepPixels = 0;
			}
			_set_move_step();
		}
		
		public function update(every_mseconds:Number = -1, size:Number = 0):void {
			_update(size);
			if (every_mseconds <= 0)
				return;
			
			clearInterval(_intervalIdUpdate);
			if (every_mseconds > 0) 
				_intervalIdUpdate = setInterval(_update, every_mseconds, size);
		}
		
		private function _update(size:Number = 0):void {
			_target_mc.width;
			_target_mc.height;
			clearInterval(_intervalIdM);
			
			if (_stage) {
				_stage.removeEventListener(MouseEvent.MOUSE_WHEEL, _mouseMouseWheel);
				_stage.removeEventListener(KeyboardEvent.KEY_DOWN, _keyboardHandler);
			}
			_target_size = (size > 0) ? size : _getWidth_Height(_target_mc);
			_set_move_step();
			if (_scrollerBox && _scrollerBox.seeker_btn) {
				_set_seeker_step();
				_calcSeekerPosition();
				_fixPointerPosition();
			}
			var _target_mask_W_H:Number = _getWidth_Height(_target_mask_mc);
			
			if (_target_size > _target_mask_W_H) {
				if (_scrollerBox)
					_scrollerBox.visible = true;
				if (_keyboardEnable && _stage)
					_stage.addEventListener(KeyboardEvent.KEY_DOWN, _keyboardHandler);
				
				if (_scrollerBox && ((_scrollerBox.seeker_btn && _scrollerBox.space_btn) || _scrollerBox.up_btn || _scrollerBox.down_btn)) {
					if (_scaleSeeker) 
						_scrollerBox.seeker_btn.height = Math.max(_min_height_seeker, _target_mask_W_H / _target_size * _scrollerBox.space_btn.height);
					_aMouseGesture.enable = _mouseGestureEnable && enable;
					_aMouseGesture.minSize = _target_mask_W_H * _scroll_part;
					if (_stage)
						_stage.addEventListener(MouseEvent.MOUSE_WHEEL, _mouseMouseWheel);
					_intervalIdM = setInterval(_moveContent, _delay);
				} else {
					_intervalIdM = setInterval(_flyContent, _delay);
				}
				
				var min_position:Number = _getX_Y(_target_mask_mc) + _content_difference_size();
				if (_getX_Y(_target_mc) < min_position) {
					_position = min_position;
					_setX_Y(_target_mc, _position);
				}
			} else {
				_aMouseGesture.enable = false;
				if (_scrollerBox)
					_scrollerBox.visible = false;
				_setX_Y(_target_mc, _getX_Y(_target_mask_mc))
			}
			if (!_enable) {
				clearInterval(_intervalIdM);
				if (_stage)
					_stage.removeEventListener(MouseEvent.MOUSE_WHEEL, _mouseMouseWheel);
				_target_mc.removeEventListener(KeyboardEvent.KEY_DOWN, _keyboardHandler);
			}
		}
		
		public function positionPercent(value:Number):void {
			value = Calc.inInterval(0, 1, value) || 0;
			if (_scrollerBox && _scrollerBox.seeker_btn) {
				_moveSeeker(value * (_scrollerBox.space_btn.height - _scrollerBox.seeker_btn.height) - _scrollerBox.seeker_btn.y + _scrollerBox.space_btn.y)
			} else {
				_position = - (value * (_target_size - _getWidth_Height(_target_mask_mc)));
			}
		}
		
		private function _getX_Y(target:Object):Number {
			if (_orientation == Orientation.HORIZONTAL)
				return target['x'];
			return target['y'];
		}
		
		private function _setX_Y(target:Object, value:Number):void {
			if (_orientation == Orientation.HORIZONTAL)
				target['x'] = value;
			else 
				target['y'] = value;
		}
		
		private function _getWidth_Height(target:Object):Number {
			if (_orientation == Orientation.HORIZONTAL)
				return target['width'];
			return target['height'];
		}
		
		private function _setWidth_Height(target:Object, value:Number):void {
			if (_orientation == Orientation.HORIZONTAL)
				target['width'] = value;
			else 
				target['height'] = value;
		}
		
		private function _setControlActions():void {
			if (!_scrollerBox) return;
			if (_scrollerBox.seeker_btn && _scrollerBox.space_btn) {
				_scrollerBox.seeker_btn.addEventListener(MovieClipButtonEvent.MOUSE_DOWN, _controlsActionsHandler);
				_scrollerBox.seeker_btn.addEventListener(MovieClipButtonEvent.RELEASE, _controlsActionsHandler);
				_scrollerBox.seeker_btn.addEventListener(MovieClipButtonEvent.RELEASE_OUTSIDE, _controlsActionsHandler);
				
				_scrollerBox.space_btn.addEventListener(MovieClipButtonEvent.MOUSE_DOWN, _controlsActionsHandler);
				_scrollerBox.space_btn.addEventListener(MovieClipButtonEvent.MOUSE_PRESSING, _controlsActionsHandler);
				_scrollerBox.space_btn.useHandCursor = false;
			}
			if (_scrollerBox.up_btn) {
				_scrollerBox.up_btn.repeatDalay = _delay_button;
				_scrollerBox.up_btn.addEventListener(MovieClipButtonEvent.MOUSE_DOWN, _controlsActionsHandler);
				_scrollerBox.up_btn.addEventListener(MovieClipButtonEvent.MOUSE_PRESSING, _controlsActionsHandler);
			}
			
			if (_scrollerBox.down_btn) {
				_scrollerBox.down_btn.repeatDalay = _delay_button;
				_scrollerBox.down_btn.addEventListener(MovieClipButtonEvent.MOUSE_DOWN, _controlsActionsHandler);
				_scrollerBox.down_btn.addEventListener(MovieClipButtonEvent.MOUSE_PRESSING, _controlsActionsHandler);
			}
		}
		
		private function _resetControlActions():void {
			if (!_scrollerBox) return;
			if (_scrollerBox.seeker_btn && _scrollerBox.space_btn) {			
				_scrollerBox.seeker_btn.removeEventListener(MovieClipButtonEvent.MOUSE_DOWN, _controlsActionsHandler);
				_scrollerBox.seeker_btn.removeEventListener(MovieClipButtonEvent.RELEASE, _controlsActionsHandler);
				_scrollerBox.seeker_btn.removeEventListener(MovieClipButtonEvent.RELEASE_OUTSIDE, _controlsActionsHandler);
				
				_scrollerBox.space_btn.removeEventListener(MovieClipButtonEvent.MOUSE_DOWN, _controlsActionsHandler);
				_scrollerBox.space_btn.removeEventListener(MovieClipButtonEvent.MOUSE_PRESSING, _controlsActionsHandler);
			}
			if (_scrollerBox.up_btn) {
				_scrollerBox.up_btn.removeEventListener(MovieClipButtonEvent.MOUSE_DOWN, _controlsActionsHandler);
				_scrollerBox.up_btn.removeEventListener(MovieClipButtonEvent.MOUSE_PRESSING, _controlsActionsHandler);
			}
			
			if (_scrollerBox.down_btn) {
				_scrollerBox.down_btn.removeEventListener(MovieClipButtonEvent.MOUSE_DOWN, _controlsActionsHandler);
				_scrollerBox.down_btn.removeEventListener(MovieClipButtonEvent.MOUSE_PRESSING, _controlsActionsHandler);
			}
		}
		
		private function _controlsActionsHandler(event:MovieClipButtonEvent):void {
			var eventBtn:MovieClipButton = event.target as MovieClipButton;
			var eventType:String = event.type;
			if (_scrollerBox.seeker_btn && _scrollerBox.seeker_btn == eventBtn) {
				switch (eventType) {
					case MovieClipButtonEvent.MOUSE_DOWN: 
						clearInterval(_intervalId);
						eventBtn.startDrag(false, new Rectangle(_scrollerBox.space_btn.x, _scrollerBox.space_btn.y, 0, _scrollerBox.space_btn.height - _scrollerBox.seeker_btn.height));
						_intervalId = setInterval(_calcSeekerPosition, _delay);
					break;
					
					case MovieClipButtonEvent.RELEASE: 
					case MovieClipButtonEvent.RELEASE_OUTSIDE: 
						eventBtn.stopDrag();
						clearInterval(_intervalId);
					break;
				}
				return;
			}
			
			switch (eventType) {
				case MovieClipButtonEvent.MOUSE_PRESSING: 
				case MovieClipButtonEvent.MOUSE_DOWN: 
					if (_scrollerBox.space_btn && _scrollerBox.space_btn == eventBtn) {
						_flySeeker();
						return;
					}
					if (_scrollerBox.up_btn && _scrollerBox.up_btn == eventBtn) {
						moveUp();
						return;
					}
					if (_scrollerBox.down_btn && _scrollerBox.down_btn == eventBtn) {
						moveDown();
						return;
					}
				break;
			}
		}
		
		private function _set_orientation(orientation):void {
			if (orientation != _orientation) {
				switch (orientation) {
					case Orientation.HORIZONTAL: 
						_x_y_mouse = 'mouseX';
					break;
					case Orientation.VERTICAL: default: 
						_x_y_mouse = 'mouseY';
					break; 
					
					case Orientation.FULL:  
						trace('Orientation.' + Orientation.FULL + " don't use");
					break; 
				}
				_orientation = orientation;
			}
		}
		
		private function _set_move_step():void {
			if (!_stepInPixels) {
				var m:Number = _getWidth_Height(_target_mask_mc) * _scroll_part;
				_move_step = m / (_target_size - _getWidth_Height(_target_mask_mc));
			} else {
				//_move_step = 
			}
		}
		
		private function _set_seeker_step():void {
			_seeker_step = (_scrollerBox.space_btn.height - _scrollerBox.seeker_btn.height) * _move_step;
		}
		
		private function _mouseMouseWheel(event:MouseEvent):void {
			if (!_inSpace()) 
				return;
			
			if (event.delta > 0) 
				moveUp();
			else 
				moveDown();
		}
		
		private function _keyboardHandler(event:KeyboardEvent):void {
			if (!_inSpace() || !_keyboardEnable) 
				return;
			
			var keyCode:Number = event.keyCode;
			switch(keyCode) {
				case Keyboard.UP:
					if (_orientation == Orientation.VERTICAL)
						moveUp();
				break;
				
				case Keyboard.DOWN:
					if (_orientation == Orientation.VERTICAL)
						moveDown();
				break;
				
				case Keyboard.LEFT :
					if (_orientation == Orientation.HORIZONTAL)
						moveUp();
				break;
				
				case Keyboard.RIGHT:
					if (_orientation == Orientation.HORIZONTAL)
						moveDown();
				break;
			}
		}
		
		private function _mouseGestureHandler(event:MouseGestureEvent):void {
			if (!_enable) 
				return;
			var speed:Number = event.lenght / _aMouseGesture.delay;
			switch(event.type) {
				case MouseGestureEvent.SLIDE_DOWN: 
					if (_orientation == Orientation.VERTICAL) 
						moveUp();
				break;
				
				case MouseGestureEvent.SLIDE_UP: 
					if (_orientation == Orientation.VERTICAL) 
						moveDown();
				break;
				
				case MouseGestureEvent.SLIDE_RIGHT: 
					if (_orientation == Orientation.HORIZONTAL) 
						moveUp();
				break;
				
				case MouseGestureEvent.SLIDE_LEFT: 
					if (_orientation == Orientation.HORIZONTAL) 
						moveDown();
				break;
				
				/*case MouseGestureEvent.START: 
					trace("start")
				break;*/
				
				case MouseGestureEvent.DRAG: 
					/*var moveTo:Number =  event.dY;
					if (_orientation == Orientation.HORIZONTAL) 
						moveTo = event.dX;
					var dPercent:Number = moveTo * 100 / ((_target_size - _getWidth_Height(_target_mask_mc)));
					positionPercent((_position * 100 - dPercent) / 100);*/
				break;
			}
		}
		
		private function _flyContent():void {
			var target_X_Y:Number = _getX_Y(_target_mc);
			if (_inSpace()) {
				var parent_mc:DisplayObjectContainer = _target_mask_mc.parent;
				var target_mask_W_H:Number = _getWidth_Height(_target_mask_mc);
				var start:Number = _getX_Y(_target_mask_mc);
				var end:Number = _target_size - target_mask_W_H - start;
				
				if (_keepMouse) 
					_position = -((parent_mc[_x_y_mouse] - start) / target_mask_W_H * (_target_size - target_mask_W_H)) + start;
				else 
					_position = target_X_Y - (parent_mc[_x_y_mouse] - (target_mask_W_H * .5 + start)) * (_move_step * _ratio);
				
				_position = Calc.inInterval( -end, start, _position);
				
				if (_rounded)
					_position = Math.round(_position);
			}
			
			_setX_Y(_target_mc, Calc.deltaRatioResult(_position, target_X_Y, _ratio));
		}
		
		private function _moveContent():void {
			var target_X_Y:Number = _getX_Y(_target_mc);
			var contentSize:Number = _content_difference_size();
			var position:Number = contentSize - (contentSize * (1 - _position));
			if (_rounded)
				position = Math.round(position);
			_setX_Y(_target_mc, target_X_Y + (position - target_X_Y  + _getX_Y(_target_mask_mc)) * _ratio);
		}
		
		private function _moveSeeker(delta:Number):void {
			_scrollerBox.seeker_btn.y += delta;
			_fixPointerPosition();
			_calcSeekerPosition();
		}
		
		private function _flySeeker():void {
			var step:Number = (_scrollerBox.seeker_btn.parent.mouseY - _scrollerBox.seeker_btn.y - _scrollerBox.seeker_btn.height * .5) * (_ratio * .5 );
			_moveSeeker(step);
		}
		
		private function _inSpace():Boolean {
			/*var isHits:Array = new Array() 
			var i:Number = _spaces_mcArray.length;
			var result:Boolean = false;
			while (i--) {
				var item = _spaces_mcArray[i]
				if (_Sprite.hitTestBoundsMouse(item))
					isHits.push(item);
			}
			trace(isHits);
			i = _spaces_mcArray.length
			while (i--) {
				
			}
			return result;*/
			return _Sprite.hitTestBoundsMouse(_target_mask_mc);
		}
		
		private function _calcSeekerPosition():void {
			var contentSize:Number = _content_difference_size();
			
			_position = (_scrollerBox.seeker_btn.y - _scrollerBox.space_btn.y) / (_scrollerBox.space_btn.height - _scrollerBox.seeker_btn.height);
		}
		
		private function _fixPointerPosition():void {
			var maxDown:Number = _scrollerBox.space_btn.height + _scrollerBox.space_btn.y - _scrollerBox.seeker_btn.height;
			_scrollerBox.seeker_btn.y = Calc.inInterval(_scrollerBox.space_btn.y, maxDown, _scrollerBox.seeker_btn.y);
		}
		
		private function _content_difference_size():Number {
			return _getWidth_Height(_target_mask_mc) - _target_size;
		}
		
		private function _scroll_difference_size():Number {
			return _scrollerBox.space_btn.height - _scrollerBox.seeker_btn.height;
		}
		
		override public function set enable(value:Boolean):void {
			if (value != _enable) {
				super.enable = value;
				update();
			}
		}
		
		public function get mouseGesture():MouseGesture { return _aMouseGesture; }
		
		public function get target_size():Number { return _target_size; }
		public function set target_size(value:Number):void {
			_target_size = value;
		}
		
		public function remove():void {
			_resetControlActions();
			_Array.remove(_target_mask_mc, _spaces_mcArray);
			clearInterval(_intervalId);
			clearInterval(_intervalIdM);
			clearInterval(_intervalIdUpdate);
			_target_mc.removeEventListener(KeyboardEvent.KEY_DOWN, _keyboardHandler);
			if (_stage)
				_stage.removeEventListener(MouseEvent.MOUSE_WHEEL, _mouseMouseWheel);
			_aMouseGesture.removeEventListener(MouseGestureEvent.START, _mouseGestureHandler);
			_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_DOWN, _mouseGestureHandler);
			_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_UP, _mouseGestureHandler);
			_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_RIGHT, _mouseGestureHandler);
			_aMouseGesture.removeEventListener(MouseGestureEvent.SLIDE_LEFT, _mouseGestureHandler);
			_aMouseGesture.removeEventListener(MouseGestureEvent.DRAG, _mouseGestureHandler);
			_aMouseGesture.remove();
		}
	}

}